package org.pixelami.twisted.spread.pb
{
	//import org.pixelami.twisted.internet.defer.Deferred;
	import org.pixelami.twisted.internet.defer.*;
	import org.pixelami.twisted.internet.protocol.ClientFactory;
	
	public class PBClientFactory extends ClientFactory
	{
		/*
		Client factory for PB brokers.

	    As with all client factories, use with reactor.connectTCP/SSL/etc..
	    getPerspective and getRootObject can be called either before or
	    after the connect.
	    */
	
	    private var _protocol:Object = Broker;
	    //private var _protocol:Object = AMFBroker;
	    
	    override public function set protocol(p:Object):void
	    {
	    	_protocol = p;
	    }
	    override public function get protocol():*
	    {
	    	return _protocol;
	    }
	    
	    private var unsafetracebacks:uint = 0;
	    
	    private var _broker:Object;
	    private var _root:Object;
	    
	    private var rootObjectRequests:Array;
		
		
		
		
		public function PBClientFactory()
		{
			this._reset()
		}
	        
	
	    private function _reset():void
	    {
	        this.rootObjectRequests = [] //# list of deferred
	        this._broker = null;
	        this._root = null;
	    }
	
	    private function _failAll(reason:Object):void
	    {
	        var deferreds:Array = this.rootObjectRequests;
	        this._reset();
	        for each(var d:Deferred in deferreds)
	        {
	            // TODO - This is smelly
	            d.errback(Function(reason));
	        }
	    }
	
	    override public function clientConnectionFailed(connector:Object, reason:Object):void
	    {
	        this._failAll(reason);
	    }
	
	    override public function clientConnectionLost(connector:Object, reason:Object, reconnecting:Boolean = false):void
	    {
	        /*
	        Reconnecting subclasses should call with reconnecting=1.
			*/
	        if (reconnecting)
	        {
	            //# any pending requests will go to next connection attempt
	            //# so we don't fail them.
	            this._broker = null;
	            this._root = null;
	        }
	        else
	        {
	            this._failAll(reason)
	        }
	    }
	
	    public function clientConnectionMade(broker:Object):void
	    {
	        this._broker = broker;
	        
	        // this is supposed to happen in Factory.buildProtocol()
	        // but I can't figure out how to get all that working yet so we're
	        // going to set the factory here while we've got a reference to the broker instance
	        this._broker.factory = this;
	        
	        this._root = broker.remoteForName("root");
	        trace("client connection made - We have a remote root Reference- "+this._root);
	        var ds:Array = this.rootObjectRequests.slice();
	        this.rootObjectRequests = [];
	        //trace("queued Deferreds count = "+ds.length);
	        for each(var d:Deferred in ds)
	        {
	            d.callback(this._root);
	        }
	    }
	
	    public function getRootObject():Deferred
	    {
	        /*
	        Get root object of remote PB server.
	
	        @return: Deferred of the root object.
	        */
	        if(this._broker && !this._broker.disconnected)
	        {
	           trace("getRootObject - succeed");
	           return Deferred.succeed(this._root);
	        }
	        var d:Deferred = new Deferred();
	        this.rootObjectRequests.push(d);
	        return d;
	    }
	
	    public function disconnect():void
	    {
	        /*
	        If the factory is connected, close the connection.
	
	        Note that if you set up the factory to reconnect, you will need to
	        implement extra logic to prevent automatic reconnection after this
	        is called.
	        */
	        if (this._broker)
	            this._broker.transport.loseConnection();
	    }
	
	    public function _cbSendUsername(rootObject:Object, username:String, password:String, client:Object):Deferred
	    {
	        trace("_cbSendUsername - "+rootObject+" , "+username+" , "+password+" , "+client);
	        ////trace("rootObject.callRemote = "+rootObject.callRemote);
	        return rootObject.callRemote("login", username).addCallback(
	            this._cbResponse, password, client)
	        //var d:Deferred = rootObject.callRemote("login", username);
	        //d.addCallback(this._cbResponse, password, client);
	        ////trace("_cbSendUsername Deferred id : "+d.ref_id);
	        //return d;
	    }
	
	    public function _cbResponse(challengTuple:Array, password:String, client:Object):Deferred
	    {
	        var challenge:String = challengTuple[0];
	        var challenger:RemoteReference = challengTuple[1];
	        
	        //challengTuple.unpack(challenge, challenger);
	        //trace("_cbResponse = "+challenge);
	        //var cr1 = Utils.respondToChallenge(challenge, password);
	        var cr2:String = Utils.respond(challenge, password);
	        
	        //var cr:ChallengeResponse = new ChallengeResponse(challenge, password);
	        
	        return challenger.callRemote("respond", cr2 , client)
		}
	
	    public function login(credentials:Object, client:Object = null):Deferred
	    {
	        /*
	        Login and get perspective from remote PB server.
	
	        Currently only credentials implementing
	        L{twisted.cred.credentials.IUsernamePassword} are supported.
	
	        @return: Deferred of RemoteReference to the perspective.
	        */
	        var d:Deferred = this.getRootObject();
	        trace("login rootObject d id = "+d.ref_id);
	        d.addCallback(this._cbSendUsername, credentials.username, credentials.password, client);
	        return d;
	    }
	}
}